package mail

import (
	"crypto/tls"
	"fmt"
	"net"
	"net/smtp"
	"strings"

	"gitee.com/shoyer/logger"
	"gitee.com/shoyer/util"
)

// for more info: http://www.361way.com/golang-send-email/5853.html
var (
	config *Config
)

type Config struct {
	From         string
	SmtpServer   string
	Nickname     string
	Password     string
	ExtraContent string
	ContentType  string
	Debug        bool
}

func Init(c *Config) error {
	config = c
	return nil
}

func checkConfig() {
	if config == nil {
		logger.Fatalf("mail config not ok, pls check!!!")
	}
}

// Send Send
func Send(to, subject, content string) (bool, error) {
	// TODO: 这里有钱后要换成no_reply账号发送。
	checkConfig()
	smtpPort := "25"
	auth := smtp.PlainAuth("", config.From, config.Password, config.SmtpServer)
	content += config.ExtraContent

	msg := []byte("To: " + to + "\r\nFrom: " + config.Nickname +
		"<" + config.From + ">\r\nSubject: " + encodeSubject(subject) + "\r\n" + config.ContentType + "\r\n\r\n" + content)

	if config.Debug {
		logger.Debugf("send mail start: %s, %s, %s", to, subject, content)
	}

	err := smtp.SendMail(config.SmtpServer+":"+smtpPort, auth, config.From, []string{to}, msg)
	if err != nil {
		logger.Errorf("send mail error: %s, %s, %s, %v", to, subject, content, err)
		return false, err
	}

	if config.Debug {
		logger.Debugf("send mail success: %s, %s, %s", to, subject, content)
	}

	return true, nil
}

// SendSSL SendSSL
func SendSSL(to, subject, content string) (bool, error) {
	checkConfig()
	content += config.ExtraContent

	if config.Debug {
		logger.Debugf("send mail start: %s, %s, %s", to, subject, content)
	}

	client := New(config.From, config.Password, config.Nickname, config.SmtpServer, 465, true)
	if err := client.SendEmail(util.Split(to, ","), subject, content); err != nil {
		logger.Errorf("send mail error: %s, %s, %s, %v", to, subject, content, err)
		return false, err
	}

	if config.Debug {
		logger.Debugf("send mail success: %s, %s, %s", to, subject, content)
	}

	return true, nil
}

// Client simple email client support ssl , and from https://blog.csdn.net/zhnxin_163/article/details/81363242
// 用ssl的原因 https://blog.csdn.net/weixin_41933666/article/details/81001914
type Client struct {
	user     string
	addr     string
	nickName string
	isSSL    bool
	auth     smtp.Auth
}

// New new email client
func New(user, password, nickName, host string, port int, isSsl bool) *Client {
	ec := &Client{
		user:  user,
		addr:  fmt.Sprintf("%s:%d", host, port),
		isSSL: isSsl,
		auth:  smtp.PlainAuth("", user, password, host),
	}
	if nickName == "" {
		ec.nickName = user
	} else {
		ec.nickName = nickName
	}
	return ec
}

func (ec *Client) generateEmailMsg(toUser []string, subject, content string) []byte {
	return ec.generateEmailMsgByte(toUser, subject, []byte(content))
}

func (ec *Client) generateEmailMsgByte(toUser []string, subject string, body []byte) []byte {
	msgStr := fmt.Sprintf("To: %s\r\nFrom: %s<%s>\r\nSubject: %s\r\n"+config.ContentType+"\r\n\r\n",
		strings.Join(toUser, ","), ec.nickName, ec.user, encodeSubject(subject))
	return append([]byte(msgStr), body...)
}

func (ec *Client) sendMailTLS(toUser []string, msg []byte) error {
	host, _, _ := net.SplitHostPort(ec.addr)
	tlsconfig := &tls.Config{
		InsecureSkipVerify: true,
		ServerName:         host,
	}
	conn, err := tls.Dial("tcp", ec.addr, tlsconfig)
	if err != nil {
		return fmt.Errorf("DialConn:%v", err)
	}
	client, err := smtp.NewClient(conn, host)
	if err != nil {
		return fmt.Errorf("Client:generateClient:%v", err)
	}
	defer client.Close()
	if ec.auth != nil {
		if ok, _ := client.Extension("AUTH"); ok {
			if err = client.Auth(ec.auth); err != nil {
				return fmt.Errorf("Client:clientAuth:%v", err)
			}
		}
	}
	if err = client.Mail(ec.user); err != nil {
		return fmt.Errorf("Client:clientMail:%v", err)
	}

	for _, addr := range toUser {
		if err = client.Rcpt(addr); err != nil {
			return fmt.Errorf("Client:Rcpt:%v", err)
		}
	}
	w, err := client.Data()
	if err != nil {
		return fmt.Errorf("Client:%v", err)
	}
	_, err = w.Write(msg)
	if err != nil {
		return fmt.Errorf("Client:WriterBody:%v", err)
	}
	err = w.Close()
	if err != nil {
		return fmt.Errorf("Client:CloseBody:%v", err)
	}
	return client.Quit()
}

func (ec *Client) sendMail(toUser []string, msg []byte) error {
	return smtp.SendMail(ec.addr, ec.auth, ec.user, toUser, msg)
}

// SendEmail send email by string content
func (ec *Client) SendEmail(toUser []string, subject string, content string) error {
	checkConfig()
	msg := ec.generateEmailMsg(toUser, subject, content)
	if ec.isSSL {
		return ec.sendMailTLS(toUser, msg)
	}
	return ec.sendMail(toUser, msg)
}

// SendEmailByte send email by byte body
func (ec *Client) SendEmailByte(toUser []string, subject string, body []byte) error {
	checkConfig()
	msg := ec.generateEmailMsgByte(toUser, subject, body)
	if ec.isSSL {
		return ec.sendMailTLS(toUser, msg)
	}
	return ec.sendMail(toUser, msg)
}

// for more: https://www.itdaan.com/blog/2015/07/01/c6b03e53a432320623df305ce5ad6dc0.html
// 最近有用户反馈在mac下通过airmail、outlook这样的邮件客户端查看系统发出邮件的标题显示为乱码，但是windows下正常。

// 通过邮件客户端保存为eml，用sublime text 2打开，可以发现展示为以下效果：

// Date: Wed, 01 Jul 2015 19:54:57 +0800
// From: <此处省略>
// To: "=?GBK?B?zt6+zA==?=" <此处省略>
// Subject: [MTLÑÐ·¢Ö§³Å] ÏîÄ¿×´Ì¬Í¨Öª
// X-Mailer: smtp-cli 3.6, see http://smtp-cli.logix.cz
// Message-Id: 1435751697-132512@smtp-cli
// Subject直接传入了中文内容，不符合邮件MIME规范。windows显示正常，不过是因为系统本身良好的兼容性而已。

// 想要编码相应的文字，只需要按照以下格式：

// =?charset?encoding?encoded text?=
// encoding有两个可选值：Q和B，前者表示QP编码（Quoted-printable），后者为BASE64编码。

// 我们选择BASE64作为编码方案，处理一下Subject：

//	private String encodeSubject(String subject) {
//	        byte[] base64 = org.apache.commons.codec.binary.Base64.encodeBase64(subject.getBytes());
//	        return String.format("=?UTF-8?B?%s?=", new String(base64));
//	    }
//
// 经过这么处理，发送出去的邮件中的Subject不再是中文，乱码问题也就随之解决。
func encodeSubject(s string) string {
	return "=?UTF-8?B?" + util.Base64(s) + "?="
}
